#include<iostream>
#include<vector>
#include<functional>
#include<unordered_map>
#include<cstdint>
#include<memory>
#include<unistd.h>
//定时器任务类---即每一个定时器的任务
using TaskFunc = std::function<void()>;
using ReleaseFunc = std::function<void()>;
class TimerTask
{
private:
    uint64_t _id;//每一个任务都需要有id来标识它，以便后续的操作
    uint64_t _timeout;//定时任务的超时时间---这个时间一旦超时，那么这个任务就会被执行
    TaskFunc _task_cb;//定时器对象要执行的定时任务
    ReleaseFunc _release;//用于删除TimerWheel中保存的定时器对象信息
    bool _canceled;//false--表示没有被取消，true表示被取消
public:
    TimerTask(uint64_t id,uint64_t delay,const TaskFunc &cb)
        :_id(id)
        ,_timeout(delay)
        ,_task_cb(cb)
        ,_canceled(false)
    {}
    //定时器对象销毁时，即时间超时，执行任务
    ~TimerTask()
    {
        if(_canceled==false) _task_cb();//这个任务没有被取消，才能去执行，被取消了执行不了
        _release();
    }

    void Cancel(){_canceled = true;}

    //设置删除定时器对象信息的函数
    void SetRelease(const ReleaseFunc &cb){_release = cb;}
    uint64_t GetDelayTime(){return _timeout;}
};
//时间轮
class TimerWheel
{
private:
    using PtrTask = std::shared_ptr<TimerTask>;
    using WeakTask = std::weak_ptr<TimerTask>;
    int _tick;//走到哪，执行到哪个任务
    int _capacity;//时间轮最大数量，就是最大延迟时间
    std::vector<std::vector<PtrTask>> _wheel;//时间轮---二维数组--？？？---存放的是使用智能指针管理起来的定时任务
    std::unordered_map<uint64_t,WeakTask> _timers;//用于通过id找到定时器任务，使用weak_ptr来管理定时器任务对象，与shared_ptr共享计数
private:
    void RemoveTimer(uint64_t id)
    {
        auto it = _timers.find(id);
        if(it!=_timers.end())
        {
            _timers.erase(id);
        }
    }
public:
    TimerWheel()
        :_tick(0)
        ,_capacity(60)
        ,_wheel(_capacity)
    {}
    //添加定时任务,添加到_timers映射起来---需要的是定时任务的id、延迟时间和执行的任务
    void TimerAdd(uint64_t id,uint64_t delay,const TaskFunc &cb)
    {
        //步骤：1.new一个定时任务，使用shared_ptr进行管理  2.设置删除方法  3.使用哈希映射起来 4.添加到时间轮中
        PtrTask pt(new TimerTask(id,delay,cb));
        pt->SetRelease(std::bind(&TimerWheel::RemoveTimer,this,id));
        //在使用哈希映射时，不能使用shared_ptr，因为这样会永久保存着一个shared_ptr的对象，计数不会变为0
        //而是使用weak_ptr，与时间轮的任务（对应起来的那个）共享计数，当任务执行，计数减1，那么在哈希中的weak_ptr
        //的计数也跟着减1，从而删除.
        _timers[id] = WeakTask(pt);
        int pos = (_tick+delay)%_capacity;
        _wheel[pos].push_back(pt);
    }

    //延迟/刷新定时任务----需要的是需要刷新或延迟的任务的id
    void TimerRefresh(uint64_t id)
    {
        //通过保存的定时器对象的weak_ptr构造一个shared_ptr出来，添加到时间轮中
        auto it = _timers.find(id);
        if(it==_timers.end())
        {
            return;//没找到定时任务，无法刷新
        }
        //找到后，构造
        PtrTask pt=it->second.lock();
        int delay = pt->GetDelayTime();
        int pos = (_tick+delay)%_capacity;
        _wheel[pos].push_back(pt);
    }

    //指针往后走啦---每秒钟执行一次这个函数，相当于秒针往后走一步，走到的位置的任务进行执行
    void RunTimerTask()
    {
        _tick = (_tick+1)%_capacity;
        _wheel[_tick].clear();//清空指针指定的位置，这样就会把数组中保存的所有管理定时器对象的shared_ptr释放掉
    }

    //取消定时任务
    void TimerCancel(uint64_t id)
    {
        auto it = _timers.find(id);
        if(it==_timers.end())
        {
            return;//没找到定时任务，无法刷新
        }
        PtrTask pt=it->second.lock();
        if(pt) pt->Cancel();
    }
};
class Test
{
    public:
        Test(){std::cout<<"构造"<<std::endl;}
        ~Test(){std::cout<<"析构"<<std::endl;}
};

void DelTest(Test* t)
{
    delete t;
}
int main()
{
    TimerWheel tw;
    Test* t = new Test();

    tw.TimerAdd(1,5,std::bind(DelTest,t));//编号为1的任务，在5秒后，执行DelTest，释放Test对象，预测会打印出“析构”
    for(int i = 0;i<5;++i)
    {
        tw.TimerRefresh(1);//刷新定时任务
        tw.RunTimerTask();//秒针往后走
        std::cout<<"刷新了定时任务,5秒后销毁\n";
        
    }
    while(1)
    {
        sleep(1);
        std::cout<<"----------------------\n";
        tw.RunTimerTask();
    }

    return 0;
}